
    /*
        交换机模块的实现
        首先是交换机的定义
            包含1，交换机名称
                2，交换机类型（直接OR广播OR匹配）
                3，是否持久化标志
                4，是否自动删除标志
                5，其他参数
        接下来是对交换机持久化功能的实现，也就是进行写磁盘操作,使用sqlite数据库进行存储
        最后是对交换机数据的管理，比如新增一个交换机，删除一个交换机等等
    */
#ifndef __M_EXCHANGE_H__
#define __M_EXCHANGE_H__
#include "../mqcommon/helpper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/mq_message.pb.h" //这里包含交换机类型，持久化等枚举类型
#include "google/protobuf/map.h"
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
#include <mutex>

namespace xgmq
{
    // 定义交换机
    struct Exchange
    {
        std::string name;
        ExchangeType type;                                 // 交换机类型
        bool durable;                                      // 是否持久化
        bool auto_delete;                                  // 是否自动删除 1表示true,0表示false
        google::protobuf::Map<std::string,std::string> args; // 其他参数
        Exchange()
        {}
        Exchange(const std::string &ename,
                 ExchangeType etype,
                 bool edurable,
                 bool edelete,
                 const google::protobuf::Map<std::string,std::string>& eargs)
            : name(ename), type(etype), durable(edurable), auto_delete(edelete), args(eargs)
        {
        }
        // 这里规定其他参数在数据库中存储的格式为key=val&key=val&...
        // 设置其他参数
        void setArgs(const std::string &str_args)
        {
            // 将args转化为键值对的形式存储在map中
            std::vector<std::string> result;
            StrSplit::split(str_args, "&", result); // 按照&分割开，放入result数组中

            for (auto &str : result)
            {
                // 再按照=分割得到key val
                auto pos = str.find("=");
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                args[key]=val;
            }
        }
        // 与上面的操作正好相反
        std::string getArgs()
        {
            std::string result;
            for (auto start = args.begin(); start != args.end(); start++)
            {
                result += start->first + "=" + start->second + "&";
            }
            return result;
        }
    };

    using ExchangePtr = std::shared_ptr<Exchange>;
    using ExchangeMap = std::unordered_map<std::string, ExchangePtr>;
    // 交换机持久化功能的实现——将数据存储到sqlite数据库中
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string &dbfile) : _sql_help(dbfile)
        {
            // 针对传入的dbfile，判断是其路径是否存在
            FileHelper file_help(dbfile);
            std::string path = file_help.parentDirector(dbfile);
            if (file_help.isExits() == false)
            {
                file_help.createDirector(path);
            }
            // 创建/打开数据库
            assert(_sql_help.open());

            createTable();
        }
        // 创建交换机数据表
        void createTable()
        {
#define CREATE_SQL "create table if not exists exchange_table(\
                                    name varchar(32) primary key,\
                                    type int,\
                                    durable int,\
                                    auto_delete int,\
                                    args varchar(128));"
            bool ret = _sql_help.exec(CREATE_SQL, nullptr, nullptr);
            if (ret == false)
            {
                ELOG("创建表失败");
                abort();
            }
        }
        // 删除交换机数据表
        void removeTable()
        {
            std::string sql = "drop table if exists exchange_table;";
            bool ret = _sql_help.exec(sql, nullptr, nullptr);
            if (ret == false)
            {
                ELOG("删除表失败");
                abort();
            }
        }
        // 新增一个交换机
        void insert(ExchangePtr &exp)
        {
            std::stringstream ss;
            std::string str_args = exp->getArgs();
            ss << "insert into exchange_table values (";
            ss << "'" << exp->name << "',";
            ss << exp->type << ",";
            ss << exp->durable << ",";
            ss << exp->auto_delete << ",";
            ss << "'" << str_args << "')";

            bool ret = _sql_help.exec(ss.str(), nullptr, nullptr);
            if (ret == false)
            {
                ELOG("新增交换机失败");
            }
        }
        // 删除一个交换机
        void remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name=";
            ss << "'" << name << "';";
            bool ret = _sql_help.exec(ss.str(), nullptr, nullptr);
            if (ret == false)
            {
                ELOG("删除交换机失败");
            }
        }
        // 获取交换机信息，也就是所有的交换机，使用哈希表来存储
        ExchangeMap recovery()
        {
            ExchangeMap result;
            std::string sql = "select name,type,durable,auto_delete,args from exchange_table;";
            bool ret = _sql_help.exec(sql, selectCallback, &result);
            if (ret == false)
            {
                ELOG("删除交换机失败");
            }

            return result;
        }

    private:
        static int selectCallback(void *arg, int numrow, char **res, char **fields)
        {
            ExchangeMap *result = (ExchangeMap *)arg;
            ExchangePtr exp=std::make_shared<Exchange>();
            exp->name = res[0];
            exp->type = (ExchangeType)std::stoi(res[1]);
            exp->durable = (bool)std::stoi(res[2]);
            exp->auto_delete = (bool)std::stoi(res[3]);
            // 其他参数可能为空
            if (res[4])
                exp->setArgs(res[4]);

            result->insert(std::make_pair(exp->name, exp));
            return 0;
        }

    private:
        SqliteHelper _sql_help;
    };

    // 对交换机进行管理
    class ExchangeManagger
    {
    public:
        using ptr = std::shared_ptr<ExchangeManagger>;
        ExchangeManagger(const std::string &dbfile)
            : _mapper(dbfile)
        {
            // 程序一启动，通过_mapper对象从数据库中加载交换机数据，成功恢复到内存中
            _exchanges = _mapper.recovery();
        }
        // 声明一个交换机
        // 有则OK，无责创建
        void declareExchange(const std::string &ename,
                             ExchangeType etype,
                             bool edurable,
                             bool edelete,
                             const google::protobuf::Map<std::string,std::string>& eargs)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 如果该交换机已经存在，就不插入了，毕竟持久化存储是需要进行磁盘IO的
            if (_exchanges.find(ename) != _exchanges.end())
                return;
            auto exp = std::make_shared<Exchange>(ename, etype, edurable, edelete, eargs);
            if (edurable == true)
            {
                // 如果该交换机是需要持久化存储的，就在数据库中也存一份
                _mapper.insert(exp);
            }
            // 存储在内存中
            _exchanges.insert(std::make_pair(ename, exp));
        }
        // 删除一个交换机
        void deleteExchange(const std::string name)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto it = _exchanges.find(name);
            // 如果该交换机不存在，就不执行操作了
            if (it == _exchanges.end())
                return;
            // 如果该交换机是持久化存储的,也要从数据库中将其删除
            if (it->second->durable == true)
            {
                _mapper.remove(name);
            }
            // 从内存中删除
            _exchanges.erase(name);
        }
        // 获取指定的交换机
        ExchangePtr getExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
            {
                return ExchangePtr();
            }
            return it->second;
        }
        // 判断指定交换机是否存在
        bool isExits(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
            {
                return false;
            }
            return true;
        }
        // 清理所有交换机数据
        // 数据库和内存中都清理
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }
        size_t size()
        {
            return _exchanges.size();
        }

    private:
        std::mutex _mutex;
        // 操纵数据库（文件）中的数据
        ExchangeMapper _mapper;
        // 操纵内存中的数据
        ExchangeMap _exchanges; // 使用哈希表来管理交换机,key是交换机名称,val交换机的智能指针对象
    };
}
#endif